package com.tma.implementation;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.tma.model.GraphObject;
import com.tma.service.GraphStorageService;

public class FileBasedStorage implements GraphStorageService{
	
	private static final String ROOT_LOCATION = "/root/Desktop/storage";
	private static final String DOT_TXT = ".txt";
	private static final Logger LOGGER = Logger.getLogger(FileBasedStorage.class.getName());
		
	public FileBasedStorage(){}
	
	public static String getRootLocation()
	{
		return (ROOT_LOCATION);
	}
	
	/**
	 * 
	 * @param grapName: name of the graph.
	 * @return the location of the file which contains graph description.
	 */	
	private String getFileLocation(String grapName)
	{
		return (ROOT_LOCATION + "/" + grapName + DOT_TXT);
	}
	
	/**
	 * 
	 * @param fileName: name of the file contains graph description
	 * @return name of the graph.
	 */
	private String getGraphNameFromFileName(String fileName)
	{
		int index = fileName.lastIndexOf(DOT_TXT);
		String graphName = "";
		if(index != -1) // fileName is in correct format
		{
			graphName =  fileName.substring(0, index);
		}
		return graphName;
	}
	
	private boolean isFileExists(String fileName)
	{
	    File file = new File(fileName);
	    return file.exists();
	}

	private boolean deleteFile(String fileName)
	{
	    File file = new File(fileName);
	    return file.delete();
	}
		
	/**
	 * 
	 * @param fileName
	 * @param data 
	 * @return true if the operation is success otherwise return false
	 */
	private boolean writeToFile(String fileName, String data)
	{
		File outFile;
		boolean result;
		DataOutputStream dos = null;
		try{
			outFile = new File(fileName);
		    dos = new DataOutputStream(new FileOutputStream(outFile));
		    dos.writeBytes(data);
		    result = true;
		} catch (FileNotFoundException ex){
			  LOGGER.log(Level.SEVERE, "File not found while writing: " + fileName );
		      result = false;
		} catch (IOException ex){
			LOGGER.log(Level.SEVERE, "IO error while writing: " + fileName );
			result = false;
		} finally{
			if(dos != null){
				try{
					dos.close();
				}catch(Exception e){}
			}
		}
		
		return result;
	}
	/**
	 * 
	 * @param fileName
	 * @param retString: the StringBuilder object which will contains the file's content.
	 * @return true: the operation is success otherwise return false;
	 */
	private boolean readFromFile(String fileName, StringBuilder retString)
	{		
		String retBuff = "";
		boolean result = false;		
		char[] cbuf;
		File inFile = new File(fileName);
		int dataLen = (int)inFile.length();
				
		if(dataLen == 0) // file contains nothing.
		{
			result = false;
		}
		else // Read file's content
		{		
			cbuf = new char[dataLen];
			FileReader fr = null;
			
			try {
				fr = new FileReader(inFile);
				int readLen = fr.read(cbuf, 0, dataLen);
				
				if(readLen > 0)
		    	{
					retBuff = String.valueOf(cbuf);
					retString.append(retBuff);
					result = true;
		    	}
				
			} catch (FileNotFoundException e) {
				
				LOGGER.log(Level.SEVERE, "File not found while reading: " + fileName);
				result = false;
			} catch (IOException e) {
				
				LOGGER.log(Level.SEVERE, "IO error while reading: " + fileName);
				result = false;
			} finally{
				if(fr != null){
					try{
						fr.close();
					}catch(Exception e){}
				}
			}
		}
		
		return result;
	}
	
	
	/**
	 * function: Store a graph to a file.
	 * @param graph: graph object
	 * @return true: the operation is success; false: the operation is failed.
	 */	
	@Override	
	public boolean store(GraphObject graph) {
		
		String fileLocation = getFileLocation(graph.getGraphName());
		
		//If the file exists, delete it then write a new one.
		if(isFileExists(fileLocation) == true)
		{
			deleteFile(fileLocation);
		}		
		
		String graphDescription = graph.getJsonDescription();
		return writeToFile(fileLocation, graphDescription);
	}
	
	/**
	 * function: Get a graph from file base.
	 * @param graphName: name of the graph
	 * @return GraphObject if the graph exists, otherwise return null.
	 */	
	@Override
	public GraphObject retrieve(String graphName) {
		
		String fileLocation = getFileLocation(graphName);
		GraphObject retGraph = null;
				
		if(isFileExists(fileLocation) == false) // file which contains graph description does not exist
		{
			retGraph =  null;
		}
		else // Get the file's content 
		{
			String graphDescription = "";
			StringBuilder sb = new StringBuilder();
			boolean result = readFromFile(fileLocation, sb);
			
			if(result == false) // cannot read file's content
			{
				retGraph =  null;
			}
			else
			{
				graphDescription = sb.toString();
				retGraph = new GraphObject(graphName);
				retGraph.setJsonDescription(graphDescription);
			}
		}
		
		return retGraph;
	}
	
	
	/**
	 * function: Get a all graph from file base.
	 * @return an ArrayList contains all the graphs
	 */	
	@Override
	public ArrayList<GraphObject> retrieveAll() {		
		
		ArrayList<GraphObject> graphs = new ArrayList<GraphObject>();
		
		File folder = new File(ROOT_LOCATION);
		File[] listOfFiles = folder.listFiles();

		for (int i = 0; i < listOfFiles.length; i++) {
		  // Get only file with extension is ".txt"
		  if (listOfFiles[i].isFile() && listOfFiles[i].getPath().endsWith(DOT_TXT))
		  {
			  String graphName = getGraphNameFromFileName(listOfFiles[i].getName());
			  StringBuilder sb = new StringBuilder();
			  String grapDescription = "";
			  boolean result = readFromFile(listOfFiles[i].getPath(), sb);
			  
			  //cannot get graph name or graph description - skip it.
			  if(graphName.length() == 0 || result == false)
			  {
				  continue;
			  }
			  
			  grapDescription = sb.toString();
			  GraphObject aGraph = new GraphObject(graphName, grapDescription);
			  graphs.add(aGraph);
		  } 
		}
		
		return graphs;
	}
	
	/**
	 * function: delete a graph from file base.
	 * @param graphName: name of the graph
	 * @return true if success otherwise return false
	 */	
	@Override
	public boolean delete(String graphName) {
		
		String fileLocation = getFileLocation(graphName);
		boolean result = false;
		
		if(isFileExists(fileLocation) == false)
		{
			result = false;
		}
		else
		{
			result = deleteFile(fileLocation);
		}
		
		return result;
	}
	
	/**
	 * 
	 * @return true if file base is empty, otherwise return false
	 */
	@Override
	public boolean deleteAll()
	{
		File folder = new File(ROOT_LOCATION);
		File[] listOfFiles = folder.listFiles();
		boolean result = true;
		
		for (int i = 0; i < listOfFiles.length; i++)
		{
			// Get only file with extension is ".txt"
			if (listOfFiles[i].isFile() && listOfFiles[i].getPath().endsWith(DOT_TXT))
			{
				result = deleteFile(listOfFiles[i].getPath());
				if(result == false)
				{
					break;
				}
			} 
		}
		
		return result;
	}

}
